package com.motorola.dp.jenkinsmonitor.service.util.dom;

import java.lang.reflect.Field;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.motorola.dp.jenkinsmonitor.model.util.dom.DomChild;
import com.motorola.dp.jenkinsmonitor.model.util.dom.DomEntity;
import com.motorola.dp.jenkinsmonitor.model.util.exception.ConversionException;

@SuppressWarnings({ "rawtypes", "unchecked" })
@Component("domChildProcessor")
public class DomChildProcessor implements DomAnnotationProcessor {

    @Value("#{domAnnotationProcessorHelper}")
    private DomAnnotationProcessorHelper helper;

    @Value("#{domConversionService}")
    private DomConversionService conversionService;

    public void processField(Field field, Element parent, Object model) throws Exception {
        Node childElement = findChildNode(field, parent);
        Object childObject = convertChild(childElement, field.getType());
        helper.applyFieldValue(model, field, childObject);
    }

    private Node findChildNode(Field field, Element parent) throws Exception {
        DomChild childAnnotation = field.getAnnotation(DomChild.class);
        String xpath = childAnnotation.xpath();

        boolean isXpathSpecified = !DomChild.XPATH_UNSPECIFIED.equals(xpath);
        if (isXpathSpecified) {
            return findChildByXpath(parent, xpath);
        } else {
            Class childClass = field.getType();
            String nodeName = helper.getDomEntityName(childClass);
            Integer childNumber = childAnnotation.childNumber();
            return findImmediateChildNode(nodeName, parent, childNumber);
        }
    }

    private Node findChildByXpath(Element parent, String xpath) throws Exception {
        NodeList nodes = helper.findNodesByXpath(parent, xpath);
        return nodes.item(0);
    }

    private Element findImmediateChildNode(String nodeName, Element parent, int childNumber) {
        int offsetCounter = 0;
        NodeList children = parent.getChildNodes();
        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);
            if (nodeName.equalsIgnoreCase(child.getLocalName())) {
                if (offsetCounter == childNumber) {
                    return (Element) child;
                }
                offsetCounter++;
            }
        }
        return null;
    }

    private Object convertChild(Node domNode, Class childClass) throws ConversionException {
        if (childClass.getAnnotation(DomEntity.class) != null) {
            return conversionService.convert((Element) domNode, childClass);
        } else if (childClass == String.class) {
            return domNode.getNodeValue();
        } else {
            throw new ConversionException("Child entity has no mapping");
        }
    }

    public void setHelper(DomAnnotationProcessorHelper helper) {
        this.helper = helper;
    }

    public void setConversionService(DomConversionService conversionService) {
        this.conversionService = conversionService;
    }

}
